package org.etlt.job.config.xml;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.Assert;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

import java.io.IOException;
import java.util.Properties;

public class SchemaResolver implements EntityResolver {
	public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/etlt-schemas.definition";
	private static final Log logger = LogFactory.getLog(SchemaResolver.class);
	private final ClassLoader classLoader;
	private final String schemaMappingsLocation;
	private Properties schemaMappings;

	private static SchemaResolver INSTANCE = new SchemaResolver();

	public static SchemaResolver getInstance(){
		return INSTANCE;
	}

	private SchemaResolver(){
		this(SchemaResolver.class.getClassLoader());
	}

	private SchemaResolver(ClassLoader classLoader) {
		this.classLoader = classLoader;
		this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION;
	}

	private SchemaResolver(ClassLoader classLoader, String schemaMappingsLocation) {
		Assert.hasText(schemaMappingsLocation, "'schemaMappingsLocation' must not be empty");
		this.classLoader = classLoader;
		this.schemaMappingsLocation = schemaMappingsLocation;
	}
	@Override
	public InputSource resolveEntity(String publicId, String systemId) throws IOException {
		if (logger.isDebugEnabled()) {
			logger.trace("Trying to resolve XML entity with public id [" + publicId + "] and system id [" + systemId + "]");
		}

		if (systemId != null) {
			String resourceLocation = loadSchemaMapping(systemId);
			if (resourceLocation != null) {
				Resource resource = new ClassPathResource(resourceLocation, this.classLoader);
				InputSource source = new InputSource(resource.getInputStream());
				source.setPublicId(publicId);
				source.setSystemId(systemId);
				if (logger.isDebugEnabled()) {
					logger.debug("Found XML schema [" + systemId + "] in classpath: " + resourceLocation);
				}
				return source;
			}
		}
		return null;
	}

	protected synchronized String loadSchemaMapping(String systemId) {
		if (this.schemaMappings == null) {
			if (logger.isDebugEnabled())
				logger.debug("Loading schema mappings from [" + this.schemaMappingsLocation + "]");
			try {
				this.schemaMappings = PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader);

				if (logger.isDebugEnabled())
					logger.debug("Loaded schema mappings: " + this.schemaMappings);
			} catch (IOException ex) {
				throw new IllegalStateException("Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", ex);
			}
		}

		return this.schemaMappings.getProperty(systemId);
	}
}